package info.motteke.kurumi.swt;

import info.motteke.kurumi.ImageCache;
import info.motteke.kurumi.ImageHolder;
import info.motteke.kurumi.ImageUtil;
import info.motteke.util.StringUtil;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

final class SwtImageCache implements ImageCache {

    private final Map<ImageKey, File> urlToFile;

    private final List<Image> cache;

    private ImageHolder imageHolder;

    private final String baseDir;

    public SwtImageCache(String baseDir) {
        urlToFile = new HashMap<ImageKey, File>();
        cache = new ArrayList<Image>();
        this.baseDir = baseDir;
    }

    public void setImageHolder(ImageHolder holder) {
        this.imageHolder = holder;
    }

    public Image getImage(String url) {
        return getImage(url, 0, 0);
    }

    public synchronized Image getImage(String url, int x, int y) {
        File file = getFile(new ImageKey(url, x, y));
        return createImage(file);
    }

    private Image createImage(File file) {
        Image image;
        try {
            image = new Image(Display.getDefault(), new FileInputStream(file));
            cache.add(image);
            return image;
        } catch (FileNotFoundException e) {
            throw new AssertionError(e);
        }
    }

    private File getFile(ImageKey key){

        File file = urlToFile.get(key);

        if(file == null || !file.exists()){
            file = createFile(key);
            urlToFile.put(key, file);
        }
        return file;
    }

    private File createFile(ImageKey key) {

        String url = key.url();
        String size = key.isDefaultSize() ? "default" : key.width() + "x" + key.height();

        String md5 = StringUtil.md5(url);
        String suffix = url.replaceAll(".*\\.([a-z]{3,4})$", "$1");
        File dir = new File(baseDir, size);
        File file = new File(dir, md5 + "." + suffix);
        dir.mkdirs();

        if(file.exists()){
            return file;
        }

        if(!key.isDefaultSize()){
            File base = getFile(new ImageKey(key.url()));
            ImageUtil.resizeImage(base, file, key.width(), key.height(), suffix);
            return file;
        }

        InputStream is = null;
        OutputStream os = null;

        try {
            is = imageHolder.getImage(url);
            os = new BufferedOutputStream(new FileOutputStream(file));

            int length;
            byte[] buf = new byte[1024];

            while((length = is.read(buf)) != -1){
                os.write(buf, 0, length);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {

                }
            }
            if(os != null){
                try {
                    os.close();
                } catch (IOException e) {

                }
            }
        }

        return file;
    }

    public void dispose() {
        for(Image image : cache){
            image.dispose();
        }
    }

    private static class ImageKey {
        private final String url;
        private final int width;
        private final int height;

        public ImageKey(String url, int width, int height) {
            this.url = url;
            this.width = width;
            this.height = height;
        }

        public boolean isDefaultSize() {
            return width == 0 && height == 0;
        }

        public ImageKey(String url) {
            this(url, 0, 0);
        }

        public String url() {
            return url;
        }

        public int width() {
            return width;
        }

        public int height() {
            return height;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + height;
            result = prime * result + ((url == null) ? 0 : url.hashCode());
            result = prime * result + width;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ImageKey other = (ImageKey) obj;
            if (height != other.height)
                return false;
            if (url == null) {
                if (other.url != null)
                    return false;
            } else if (!url.equals(other.url))
                return false;
            if (width != other.width)
                return false;
            return true;
        }
    }
}
